//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ListenSocket.cpp : implementation file
//

//#include "stdafx.h"
#include "wintypes.h"
#include "emule.h"
#include "ListenSocket.h"
#include "opcodes.h"
#include "KnownFile.h"
#include "SharedFileList.h"
#include "UploadQueue.h"
#include "updownclient.h"
#include "ClientList.h"

#ifndef ID_SOKETTI
#define ID_SOKETTI 7772
#endif

#include <wx/listimpl.cpp>
//WX_DEFINE_LIST(SocketListL);

IMPLEMENT_DYNAMIC_CLASS(CClientReqSocket,CEMSocket)

#include "ListenSocket.h"
// CClientReqSocket
CClientReqSocket::CClientReqSocket(CPreferences* in_prefs,CUpDownClient* in_client){
	app_prefs = in_prefs;
	client = in_client;
	if (in_client)
		client->socket = this;
	theApp.listensocket->AddSocket(this);
	ResetTimeOutTimer();
	deletethis = false;
	deltimer = 0;

	SetEventHandler(*theApp.emuledlg,ID_SOKETTI);
	SetNotify(wxSOCKET_CONNECTION_FLAG|wxSOCKET_INPUT_FLAG|wxSOCKET_OUTPUT_FLAG|wxSOCKET_LOST_FLAG);
	Notify(TRUE);
}


CClientReqSocket::~CClientReqSocket(){
  // remove event handler
  SetNotify(0);
  Notify(FALSE);

	if (client)
		client->socket = 0;
	client = 0;
	theApp.listensocket->RemoveSocket(this);

	//DEBUG_ONLY (theApp.clientlist->Debug_SocketDeleted(this));
}

void CClientReqSocket::ResetTimeOutTimer(){
	timeout_timer = ::GetTickCount();
};

bool CClientReqSocket::CheckTimeOut(){
	if (::GetTickCount() - timeout_timer > CONNECTION_TIMEOUT){
		timeout_timer = ::GetTickCount();
		Disconnect();
		return true;
	}
	return false;
};

void CClientReqSocket::OnClose(int nErrorCode){
  //ASSERT (theApp.listensocket->IsValidSocket(this));
  CEMSocket::OnClose(nErrorCode);
  Disconnect();
};

void CClientReqSocket::Disconnect(){
  //AsyncSelect(0);
  byConnected = ES_DISCONNECTED;
  if (!client)
    Safe_Delete();
  else
    client->Disconnected();
};

void CClientReqSocket::Delete_Timed(){
// it seems that MFC Sockets call socketfunctions after they are deleted, even if the socket is closed
// and select(0) is set. So we need to wait some time to make sure this doesn't happens
	if (::GetTickCount() - deltimer > 30000)
		delete this;
}

void CClientReqSocket::Safe_Delete(){
  //ASSERT (theApp.listensocket->IsValidSocket(this));
  //AsyncSelect(0);
  deltimer = ::GetTickCount();
  //if (m_hSocket != INVALID_SOCKET)
  //  ShutDown(2);
  if (client)
    client->socket = 0;
  client = 0;
  byConnected = ES_DISCONNECTED;
  deletethis = true;
  Destroy();
}

bool CClientReqSocket::ProcessPacket(char* packet, uint32 size, uint8 opcode){
  try{
    /*try*/{
      if (!client && opcode != OP_HELLO)
	throw GetResString(IDS_ERR_NOHELLO);
      switch(opcode){
      case OP_HELLOANSWER:{
	theApp.downloadqueue->AddDownDataOverheadOther(size);
	client->ProcessHelloAnswer(packet,size);
	if (client)
	  client->ConnectionEstablished();
	break;
      }
      case OP_HELLO:{
	theApp.downloadqueue->AddDownDataOverheadOther(size);
	if (!client){
	  // create new client to save standart informations
	  client = new CUpDownClient(this);
	}
	client->ProcessHelloPacket(packet,size);
	// now we check if we now this client already. if yes this socket will
	// be attached to the known client, the new client will be deleted
	// and the var. "client" will point to the known client.
	// if not we keep our new-constructed client ;)
	if (theApp.clientlist->AttachToAlreadyKnown(&client,this)){
	  // update the old client informations
	  client->ProcessHelloPacket(packet,size);
	}
	else {
	  theApp.clientlist->AddClient(client);
	  client->SetCommentDirty();
	}
	
	// if IP is filtered, dont reply but disconnect...
	if (theApp.ipfilter->IsFiltered(client->GetIP())) {
	  theApp.emuledlg->AddDebugLogLine(true,GetResString(IDS_IPFILTERED).GetData(),client->GetFullIP(),theApp.ipfilter->GetLastHit().GetData());
	  client->Disconnected();
	  theApp.stat_filteredclients++;
	  break;
	}
	
	// send a response packet with standart informations
	if (client->GetClientSoft() == SO_EMULE )
	  client->SendMuleInfoPacket(false);
	
	client->SendHelloAnswer();
	if (client)
	  client->ConnectionEstablished();
	
	break;
      }
      case OP_FILEREQUEST:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	if (size == 16 || (size > 16 && client->GetExtendedRequestsVersion() > 0)){
	  if (!client->GetWaitStartTime())
	    client->SetWaitStartTime();
	  uchar reqfileid[16];
	  memcpy(reqfileid,packet,16);
	  CKnownFile* reqfile = theApp.sharedfiles->GetFileByID(reqfileid);
	  if (!reqfile){
	    // DbT:FileRequest
	    // send file request no such file packet (0x48)
	    Packet* replypacket = new Packet(OP_FILEREQANSNOFIL, 16);
	    memcpy(replypacket->pBuffer, packet, 16);
	    theApp.uploadqueue->AddUpDataOverheadFileRequest(replypacket->size);
	    SendPacket(replypacket, true);
	    // DbT:End
	    break;
	  }
	  // if wer are downloading this file, this could be a new source
	  if (reqfile->IsPartFile())
	    if( theApp.glob_prefs->GetMaxSourcePerFile() > ((CPartFile*)reqfile)->GetSourceCount() ) //<<--
	      theApp.downloadqueue->CheckAndAddKnownSource((CPartFile*)reqfile,client);
	  
	  // check to see if this is a new file they are asking for
	  if(memcmp(client->reqfileid, packet, 16) != 0)
	    client->SetCommentDirty();
	  
	  // send filename etc
	  memcpy(client->reqfileid,packet,16);
	  CMemFile* data = new CMemFile();
	  data->Write(reqfile->GetFileHash(),16);
	  uint16 namelength = (uint16)strlen(reqfile->GetFileName());
	  data->Write(&namelength,2);
	  data->Write(reqfile->GetFileName(),namelength);
	  client->ProcessUpFileStatus(packet,size);
	  Packet* packet = new Packet(data);
	  packet->opcode = OP_FILEREQANSWER;
	  delete data;
	  theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
	  SendPacket(packet,true);
	  /* done with "OP_SETREQFILEID" now
	  //send filestatus 
	  data = new CMemFile();
	  data->Write(reqfile->GetFileHash(),16);
	  if (reqfile->IsPartFile())
	  ((CPartFile*)reqfile)->WritePartStatus(data);
	  else{
	  uint32 null = 0;
	  data->Write(&null,3);
	  }
	  packet = new Packet(data);
	  packet->opcode = OP_FILESTATUS;
	  delete data;
	  theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
	  SendPacket(packet,true);
	  */
	  client->SendCommentInfo(reqfile);
	  break;
	}
	throw GetResString(IDS_ERR_WRONGPACKAGESIZE);
	break;
      }
      case OP_FILEREQANSNOFIL:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	// DbT:FileRequest
	if (size == 16) {
	  // if that client do not have my file maybe has another different
	  CPartFile* reqfile = theApp.downloadqueue->GetFileByID((uchar*)packet);
	  if (!reqfile)
	    break;
	  
	  // we try to swap to another file ignoring no needed parts files
	  if (client) switch (client->GetDownloadState()) {
	  case DS_ONQUEUE:
	  case DS_NONEEDEDPARTS:
	    if (!client->SwapToAnotherFile(false)) {
	      theApp.downloadqueue->RemoveSource(client, true);
	    }
	    break;
	  }
	  break;
	}
	throw GetResString(IDS_ERR_WRONGPACKAGESIZE);
	break;
	// DbT:End
      }
      case OP_FILEREQANSWER:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	client->ProcessFileInfo(packet,size);
	break;
      }
      case OP_FILESTATUS:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	client->ProcessFileStatus(packet,size);
	break;
      }
      case OP_STARTUPLOADREQ:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	theApp.uploadqueue->AddClientToQueue(client);
	break;
      }
      case OP_QUEUERANK:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	CSafeMemFile* data = new CSafeMemFile((BYTE*)packet,size);
	uint32 rank;
	data->Read(&rank,4);
	client->SetRemoteQueueRank(rank);
	delete data;
	break;
      }
      case OP_ACCEPTUPLOADREQ:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	if (client->GetDownloadState() == DS_ONQUEUE ){
	  client->SetDownloadState(DS_DOWNLOADING);
	  client->SendBlockRequests();
	}
	break;
      }
      case OP_REQUESTPARTS:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	CSafeMemFile* data = new CSafeMemFile((BYTE*)packet,size);
	uchar reqfilehash[16];
	data->Read(reqfilehash,16);
	Requested_Block_Struct* reqblock1 = new Requested_Block_Struct;
	Requested_Block_Struct* reqblock2 = new Requested_Block_Struct;
	Requested_Block_Struct* reqblock3 = new Requested_Block_Struct;
	data->Read(&reqblock1->StartOffset,4);
	data->Read(&reqblock2->StartOffset,4);
	data->Read(&reqblock3->StartOffset,4);
	data->Read(&reqblock1->EndOffset,4);
	data->Read(&reqblock2->EndOffset,4);
	data->Read(&reqblock3->EndOffset,4);
	memcpy(&reqblock1->FileID,reqfilehash,16);
	memcpy(&reqblock2->FileID,reqfilehash,16);
	memcpy(&reqblock3->FileID,reqfilehash,16);
	if (reqblock1->EndOffset-reqblock1->StartOffset == 0)			
	  delete reqblock1;
	else
	  client->AddReqBlock(reqblock1);
	
	if (reqblock2->EndOffset-reqblock2->StartOffset == 0)			
	  delete reqblock2;
	else
	  client->AddReqBlock(reqblock2);
	
	if (reqblock3->EndOffset-reqblock3->StartOffset == 0)			
	  delete reqblock3;
	else
	  client->AddReqBlock(reqblock3);
	delete data;
	break;
      }
      case OP_CANCELTRANSFER:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	theApp.uploadqueue->RemoveFromUploadQueue(client);
	break;
      }
      case OP_HASHSETREQUEST:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	if (size != 16)
	  throw GetResString(IDS_ERR_WRONGHPACKAGESIZE);
	client->SendHashsetPacket(packet);
	break;
      }
      case OP_HASHSETANSWER:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	if (client->GetDownloadState() != DS_REQHASHSET)
	  throw CString("unwanted hashset");
	client->ProcessHashSet(packet,size);
	break;
      }
      case OP_SENDINGPART:{
	//					theApp.downloadqueue->AddDownDataOverheadOther(0, 24);
	client->ProcessBlockPacket(packet,size);
	break;
      }
      case OP_OUTOFPARTREQS:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	break;
      }
      case OP_SETREQFILEID:{
	theApp.downloadqueue->AddDownDataOverheadFileRequest(size);
	// DbT:FileRequest
	if (size == 16){
	  if (!client->GetWaitStartTime())
	    client->SetWaitStartTime();
	  
	  CKnownFile* reqfile = theApp.sharedfiles->GetFileByID((uchar*)packet);
	  
	  if (!reqfile){
	    // send file request no such file packet (0x48)
	    Packet* replypacket = new Packet(OP_FILEREQANSNOFIL, 16);
	    memcpy(replypacket->pBuffer, packet, 16);
	    theApp.uploadqueue->AddUpDataOverheadFileRequest(replypacket->size);
	    SendPacket(replypacket, true);
	    break;
	  }
	  
	  // if we are downloading this file, this could be a new source
	  if (reqfile->IsPartFile())
	    if( theApp.glob_prefs->GetMaxSourcePerFile() > ((CPartFile*)reqfile)->GetSourceCount() )
	      theApp.downloadqueue->CheckAndAddKnownSource((CPartFile*)reqfile,client);
	  
	  // check to see if this is a new file they are asking for
	  if(memcmp(client->reqfileid, packet, 16) != 0)
	    client->SetCommentDirty();
	  
	  //send filestatus
	  memcpy(client->reqfileid,packet,16);
	  CMemFile* data = new CMemFile();
	  data->Write(reqfile->GetFileHash(),16);
	  if (reqfile->IsPartFile())
	    ((CPartFile*)reqfile)->WritePartStatus(data);
	  else {
	    uint32 null = 0;
	    data->Write(&null,3);
	  }
	  Packet* packet = new Packet(data);
	  packet->opcode = OP_FILESTATUS;
	  delete data;
	  theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
	  SendPacket(packet,true);
	  break;
	}
	throw GetResString(IDS_ERR_WRONGPACKAGESIZE);
	break;
	// DbT:End
      }
      case OP_MESSAGE:{
	theApp.downloadqueue->AddDownDataOverheadOther(size);
	uint16 length;
	memcpy(&length,packet,2);
	if (length+2 != size)
	  throw CString("invalid message packet");
	char* message = new char[length+1];
	memcpy(message,packet+2,length);
	message[length] = 0;
	//theApp.emuledlg->chatwnd->chatselector->ProcessMessage(client,message);
#warning CHATSELECTOR       
	delete[] message;
	break;
      }
      case OP_ASKSHAREDFILES:	{	// client wants to know what we have in share, let's see if we allow him to know that
	theApp.downloadqueue->AddDownDataOverheadOther(size);
	CList<void*,void*> list;
	if (theApp.glob_prefs->CanSeeShares() == 0 ||					// everybody
	    (theApp.glob_prefs->CanSeeShares() == 1 && client->IsFriend() ) )	// friend
	  {
	    CCKey bufKey;
	    CKnownFile* cur_file;
	    for (POSITION pos = theApp.sharedfiles->m_Files_map.GetStartPosition();pos != 0;){
	      theApp.sharedfiles->m_Files_map.GetNextAssoc(pos,bufKey,cur_file);
	      list.AddTail((void*&)cur_file);
	    }
	    theApp.emuledlg->AddLogLine(true,GetResString(IDS_REQ_SHAREDFILES).GetData(),client->GetUserName(),client->GetUserID(),GetResString(IDS_ACCEPTED).GetData() );
	  } else theApp.emuledlg->AddLogLine(true,GetResString(IDS_REQ_SHAREDFILES),client->GetUserName(),client->GetUserID(),GetResString(IDS_DENIED).GetData() );
	
	// now create the memfile for the packet
	uint32 iTotalCount = list.GetCount();
	CSafeMemFile tempfile(80);
	tempfile.Write(&iTotalCount, 4);
	while (list.GetCount())
	  {
	    theApp.sharedfiles->CreateOfferedFilePacket((CKnownFile*)list.GetHead(), &tempfile);
	    list.RemoveHead();
	  }
	// create a packet and send it
	Packet* replypacket = new Packet(&tempfile);
	replypacket->opcode = OP_ASKSHAREDFILESANSWER;
	theApp.uploadqueue->AddUpDataOverheadOther(replypacket->size);
	SendPacket(replypacket, true, true);
	break;
      }
      case OP_ASKSHAREDFILESANSWER:{
	theApp.downloadqueue->AddDownDataOverheadOther(size);
	client->ProcessSharedFileList(packet,size);
	break;
      }
      default:
	theApp.downloadqueue->AddDownDataOverheadOther(size);
	theApp.emuledlg->AddDebugLogLine(false,"unknown opcode: %i %x",opcode,opcode);
	
      }
    }
    /*catch(CFileException* error){
      OUTPUT_DEBUG_TRACE();
      error->Delete();	//mf
      throw GetResString(IDS_ERR_INVALIDPACKAGE);
      }*/
  }
  catch(CString error){
    if (client){
      client->SetDownloadState(DS_ERROR);	
      //TODO write this into a debugfile
      theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_CLIENTERROR)
				       ,client->GetUserName(),client->GetFullIP(),error.GetBuffer());
    }
    else
      theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_BADCLIENTACTION),error.GetBuffer());
    
    Disconnect();
    return false;
  }
  return true;
}

bool CClientReqSocket::ProcessExtPacket(char* packet, uint32 size, uint8 opcode){
	try{
		if (!client)
			throw GetResString(IDS_ERR_UNKNOWNCLIENTACTION);
		switch(opcode){
			case OP_EMULEINFO:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				client->ProcessMuleInfoPacket(packet,size);
				client->SendMuleInfoPacket(true);
				break;
			}
			case OP_EMULEINFOANSWER:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				client->ProcessMuleInfoPacket(packet,size);
				break;
			}
			case OP_COMPRESSEDPART:{
//				theApp.downloadqueue->AddDownDataOverheadOther(24);
				client->ProcessBlockPacket(packet,size,true);
				break;
			}
			case OP_QUEUERANKING:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				if (size != 12)
					throw GetResString(IDS_ERR_BADSIZE);
				uint16 newrank;
				memcpy(&newrank,packet+0,2);
				client->SetRemoteQueueFull(false);
				client->SetRemoteQueueRank(newrank);
				break;
			}
 			case OP_REQUESTSOURCES:{
				theApp.downloadqueue->AddDownDataOverheadSourceExchange(size);
				if (client->GetSourceExchangeVersion() >= 1){
					if(size != 16)
						throw GetResString(IDS_ERR_BADSIZE);
	
					//first check shared file list, then download list
					CKnownFile* file = theApp.sharedfiles->GetFileByID((uchar*)packet);
					if(!file)
						file = theApp.downloadqueue->GetFileByID((uchar*)packet);
	
					if(file) {
						DWORD dwTimePassed = ::GetTickCount() - client->GetLastSrcReqTime() + CONNECTION_LATENCY;
						bool bNeverAskedBefore = client->GetLastSrcReqTime() == 0;
	
						if( 
						    //if not complete and file is rare, allow once every 10 minutes
						    ( file->IsPartFile() &&
						     ((CPartFile*)file)->GetSourceCount() <= RARE_FILE * 2 &&
						      (bNeverAskedBefore || dwTimePassed > SOURCECLIENTREASK)
						    ) ||
						    //OR if file is not rare or if file is complete, allow every 90 minutes
						    ( (bNeverAskedBefore || dwTimePassed > SOURCECLIENTREASK * MINCOMMONPENALTY) )
						  ) {
	
							client->SetLastSrcReqTime();
							Packet* tosend = file->CreateSrcInfoPacket(client);
							if(tosend){
								theApp.uploadqueue->AddUpDataOverheadSourceExchange(tosend->size);
								SendPacket(tosend, true, true);
								theApp.emuledlg->AddDebugLogLine( false, "RCV:Source Request User(%s) File(%s)", client->GetUserName(), file->GetFileName() );
							}
						}
						}
				}
				break;
			}
 			case OP_ANSWERSOURCES:{
				theApp.downloadqueue->AddDownDataOverheadSourceExchange(size);
				/*try*/{
					CSafeMemFile data((BYTE*)packet,size);
					uchar hash[16];
					data.Read(hash,16);
					CKnownFile* file = theApp.downloadqueue->GetFileByID((uchar*)packet);
					if(file){
						if (file->IsPartFile()){
							//set the client's answer time
							client->SetLastSrcAnswerTime();
							//and set the file's last answer time
							((CPartFile*)file)->SetLastAnsweredTime();
	
							((CPartFile*)file)->AddClientSources(&data);
						}
					}
				}
				/*catch(CFileException* error){
					OUTPUT_DEBUG_TRACE();
					error->Delete();
					throw GetResString(IDS_ERR_INVALIDPACKAGE);
					}*/
				break;
			}
			case OP_FILEDESC:{
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				client->ProcessMuleCommentPacket(packet,size);
				break;
			}
			/*case OP_VERIFYUPSREQ:{
				CUpDownClient* vuclient = theApp.clientlist->VUGetRandomClient();
				if (vuclient){
					Packet* answer = new Packet(OP_VERIFYUPSANSWER,6,OP_EMULEPROT);
					uint32 ip = vuclient->GetIP();
					memcpy(answer->pBuffer,ip,4);
					uint16 udpport = vuclient->GetUDPPort();
					memcpy(answer->pBuffer,ip,4);
					theApp.uploadqueue->AddUpDataRateMSOverhead(packet->size);
					SendPacket(answer,true,true);
				}
			}
			case OP_VERIFYUPSANSWER:{
				if (size != 12)
					throw CString("invalid size (OP_VERIFYUPSANSWER)");
			}*/

			default:
				theApp.downloadqueue->AddDownDataOverheadOther(size);
				theApp.emuledlg->AddDebugLogLine(false,"unknown opcode: %i %x",opcode,opcode);
			  
		}
	}
	catch(CString error){
		theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_BADCLIENTACTION),error.GetBuffer());
		if (client) client->SetDownloadState(DS_ERROR);		
		Disconnect();
		return false;
	}
	return true;
}

void CClientReqSocket::OnInit(){
	uint8 tv = 1;
	//SetSockOpt(SO_DONTLINGER,&tv,sizeof(BOOL));
}

void CClientReqSocket::OnSend(int nErrorCode){
	ResetTimeOutTimer();
	CEMSocket::OnSend(nErrorCode);
}

void CClientReqSocket::OnError(int nErrorCode){

#ifdef _DEBUG
	if (client)
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_BADCLIENT2)
		,client->GetUserName(),client->GetFullIP(),nErrorCode);
	else
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_ERR_BADCLIENTACTION),"CClientReqSocket::OnError");			
#endif
	Disconnect();
}

CClientReqSocket::CClientReqSocket()
{
}

bool CClientReqSocket::Close()
{
  return wxSocketBase::Close();
}

void CClientReqSocket::PacketReceived(Packet* packet){
	switch (packet->prot){
		case OP_EDONKEYPROT:
			ProcessPacket(packet->pBuffer,packet->size,packet->opcode);
			break;
		case OP_PACKEDPROT:
			if (!packet->UnPackPacket()){
				assert ( false );
				break;
			}
		case OP_EMULEPROT:
			ProcessExtPacket(packet->pBuffer,packet->size,packet->opcode);
			break;
		default:;
	}
}

void CClientReqSocket::OnReceive(int nErrorCode){
	ResetTimeOutTimer();
	CEMSocket::OnReceive(nErrorCode);
}

bool CClientReqSocket::Create(){
	theApp.listensocket->AddConnection();
	//BOOL result = CAsyncSocket::Create(0,SOCK_STREAM,FD_WRITE|FD_READ|FD_CLOSE);
	OnInit();
	return TRUE; //result;
}

IMPLEMENT_DYNAMIC_CLASS(CListenSocket,wxSocketServer)


// CListenSocket
// CListenSocket member functions
CListenSocket::CListenSocket(CPreferences* in_prefs,wxSockAddress& addr)
  : wxSocketServer(addr,wxSOCKET_NOWAIT)
{
	app_prefs = in_prefs;
	opensockets = 0;
	maxconnectionreached = 0;
	m_OpenSocketsInterval = 0;
	m_nPeningConnections = 0;

	SetEventHandler(*theApp.emuledlg,ID_SOKETTI);
	SetNotify(wxSOCKET_CONNECTION_FLAG|wxSOCKET_INPUT_FLAG|wxSOCKET_OUTPUT_FLAG);
	Notify(TRUE);
}

CListenSocket::~CListenSocket(){
	Discard();
	Close();
	KillAllSockets();
}

bool CListenSocket::StartListening(){
	bListening = true;
	//return (this->Create(app_prefs->GetPort(),SOCK_STREAM,FD_ACCEPT) && this->Listen());
	return TRUE;
}

void CListenSocket::StopListening(){
	bListening = false;
	maxconnectionreached++;
}

void CListenSocket::ReStartListening(){
	bListening = true;
	if (m_nPeningConnections){
		m_nPeningConnections--;
		OnAccept(0);
	}
}

void CListenSocket::OnAccept(int nErrorCode){
  if (!nErrorCode){
    m_nPeningConnections++;
    if (m_nPeningConnections < 1){
      //assert ( false );
      m_nPeningConnections = 1;
    }
    if (TooManySockets(true) && !theApp.serverconnect->IsConnecting()){
      StopListening();
      return;
    }
    else if ( bListening == false )
      ReStartListening(); //If the client is still at maxconnections, this will allow it to go above it.. But if you don't, you will get a lowID on all servers.
    
    while (m_nPeningConnections){
      m_nPeningConnections--;
      
      CClientReqSocket* newclient = new CClientReqSocket(app_prefs);
      //if (!Accept(*newclient))
      if(!AcceptWith(*newclient,FALSE))
	newclient->Safe_Delete();
      else{
	//newclient->AsyncSelect(FD_WRITE|FD_READ|FD_CLOSE);
	newclient->OnInit();
      }
      
      AddConnection();
    }
    //		if (TooManySockets(true) && !theApp.serverconnect->IsConnecting())
    //			StopListening();
  }
}

void CListenSocket::Process(){
	POSITION pos2;
	m_OpenSocketsInterval = 0;
	opensockets = 0;
	for(POSITION pos1 = socket_list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		socket_list.GetNext(pos1);
		CClientReqSocket* cur_sock = socket_list.GetAt(pos2);
		opensockets++;

	   if (cur_sock->deletethis){
	     cur_sock->Close();
	     cur_sock->Destroy();
	   }
	   else
		socket_list.GetAt( pos2 )->CheckTimeOut();
   }
   if ( (GetOpenSockets()+5 < app_prefs->GetMaxConnections() || theApp.serverconnect->IsConnecting()) && !bListening)
	   ReStartListening();
}

void CListenSocket::RecalculateStats(){
	memset(m_ConnectionStates,0,6);
	POSITION pos1,pos2;
	for(pos1 = socket_list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
		socket_list.GetNext(pos1);
		CClientReqSocket* cur_sock = socket_list.GetAt(pos2);
		switch (cur_sock->GetConState()){
			case ES_DISCONNECTED:
				m_ConnectionStates[0]++;
				break;
			case ES_NOTCONNECTED:
				m_ConnectionStates[1]++;
				break;
			case ES_CONNECTED:
				m_ConnectionStates[2]++;
				break;
		}
   }
}

void CListenSocket::AddSocket(CClientReqSocket* toadd){
  socket_list.AddTail(toadd);
}

void CListenSocket::RemoveSocket(CClientReqSocket* todel){
  POSITION pos2,pos1;

  for(pos1 = socket_list.GetHeadPosition(); ( pos2 = pos1 ) != NULL; ){
    socket_list.GetNext(pos1);

    if ( socket_list.GetAt(pos2) == todel )
      socket_list.RemoveAt(pos2);
  }
}

void CListenSocket::KillAllSockets(){
	for (POSITION pos = socket_list.GetHeadPosition();pos != 0;pos = socket_list.GetHeadPosition()){
		CClientReqSocket* cur_socket = socket_list.GetAt(pos);
			if (cur_socket->client)
				cur_socket->client->Destroy();
			else
				cur_socket->Destroy();
	}
}

void CListenSocket::AddConnection(){
	m_OpenSocketsInterval++;
	opensockets++;
}

bool CListenSocket::TooManySockets(bool bIgnoreInterval){
	if (GetOpenSockets() > app_prefs->GetMaxConnections() || (m_OpenSocketsInterval > theApp.glob_prefs->GetMaxConperFive() && !bIgnoreInterval) ){
		return true;
	}
	else
		return false;
}

bool CListenSocket::IsValidSocket(CClientReqSocket* totest){
	return socket_list.Find(totest);
}

void CListenSocket::Debug_ClientDeleted(CUpDownClient* deleted){
	POSITION pos1, pos2;
	for (pos1 = socket_list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		socket_list.GetNext(pos1);
		CClientReqSocket* cur_sock = socket_list.GetAt(pos2);
#if 0
		if (!AfxIsValidAddress(cur_sock, sizeof(CClientReqSocket))) {
			AfxDebugBreak(); 
		}
		if (cur_sock->client == deleted){
		  AfxDebugBreak();
		}
#endif
	}
}
